
import { _decorator, Component, UITransform, Enum, AudioClip, CCFloat, EventHandler, Button, Node, Color, SpriteFrame, Sprite, Vec3, Input, UIRenderable, lerp, EventTouch, CCBoolean, CCInteger } from 'cc';
import { EDITOR } from 'cc/env';
import { App } from '../../App';

// 音效类型
enum EffectType{
    String,
    Clip,
}

const _tempColor = new Color();

enum Transition {
    NONE = 0,
    SCALE = 1,
    COLOR = 2,
    SPRITE = 3,
}

// 状态
enum State {
    NORMAL = 'normal',
    PRESSED = 'pressed',
    DISABLED = 'disabled',
}
const { ccclass, property, requireComponent, disallowMultiple, executeInEditMode, menu } = _decorator;
 
@ccclass('TouchNode')
@requireComponent(UITransform)
@disallowMultiple
@executeInEditMode
@menu('')
export class TouchNode extends Component {
    @property({type:Node,displayOrder:0,tooltip:'指定触摸节点，触摸节点状态改变时会修改此节点的 Color 或 Sprite 属性'})
    get target () { return this._target; }

    set target (value) {
        if (this._target !== value) {
            this._target = value;
            this._applyTarget();
        }
    }

    @property({type:CCBoolean,tooltip:'是否可交互，这一项未选中时，对象处在禁用状态'})
    get interactable () { return this._interactable; }
    set interactable (value) {
        this._interactable = value;
        this._updateState();
        !this._interactable && this._resetState();
    }

    set _resizeToTarget (value: boolean) { value && this._resizeNodeToTargetNode(); }

    @property({type:Enum(EffectType),tooltip:"String 填写音频文件路径\n Clip 拖放音频文件"})
    audioType:EffectType = EffectType.String;
    @property({tooltip:'音效文件名',visible(){return this.audioType === EffectType.String}})
    audioFile: string = 'click';
    @property({type: AudioClip, tooltip:'音效文件', visible(){return this.audioType === EffectType.Clip} })
    audioClip: AudioClip = null;
    @property({ type: Node, tooltip: '将从从父节点移除' })
    hideNode: Node = null;
    @property({type:CCFloat,tooltip: '点击后多久才能再次点击'})
    preventTime:number = 0.5;

    @property({type:Enum(Transition),tooltip:'对象状态变化时的过渡类型'})
    get transition () {return this._transition;}
    set transition (value: Transition) {
        if (value === Transition.SPRITE && !this.getComponent(Sprite)){ return ;}
        this._transition !== value && (this._transition = value);
    }

    // color transition
    @property({type:Color,visible(){return this._transition === Transition.COLOR;},tooltip:'普通状态对象颜色'})
    get normalColor (): Readonly<Color> {return this._normalColor;}

    set normalColor (value) {
        if (this._normalColor !== value) {
            this._normalColor.set(value);
            this._updateState();
        }
    }
    
    @property({type:Color,visible(){return this._transition === Transition.COLOR;},tooltip:'按下状态对象颜色'})
    get pressedColor (): Readonly<Color> {return this._pressColor;}
    set pressedColor (value) {this._pressColor !== value && this._pressColor.set(value);}
    
    @property({type:Color,visible(){return this._transition === Transition.COLOR;},tooltip:'禁用状态的对象颜色'})
    get disabledColor (): Readonly<Color> { return this._disabledColor; }
    set disabledColor (value) {
        if (this._disabledColor !== value) {
            this._disabledColor.set(value);
            this._updateState();
        }
    }
    
    @property({type:CCFloat,tooltip:'对象颜色变化或者缩放变化的过渡时间'})
    get duration () { return this._duration;}
    set duration (value) {this._duration === value && (this._duration = value);}
    
    @property({type:CCFloat,range:[0.1,10.0],tooltip:'当用户点击对象后，对象会缩放到一个值，这个值等于原始 scale * zoomScale',
    visible(){return this._transition === Transition.SCALE;}})
    get zoomScale () { return this._zoomScale; }
    set zoomScale (value) { this._zoomScale !== value && (this._zoomScale = value); }

    @property({type:SpriteFrame,tooltip:'普通状态的对象图资源',visible(){
        if (!this.getComponent(Sprite))return false;
        return this._transition === Transition.SPRITE;
    }})
    get normalSprite () { return this._normalSprite; }

    set normalSprite (value: SpriteFrame | null) {
        if (this._normalSprite === value) { return; }

        this._normalSprite = value;
        const sprite = this.node.getComponent(Sprite);
        if (sprite) sprite.spriteFrame = value;

        this._updateState();
    }
    
    @property({type:SpriteFrame,tooltip:'按下状态的对象图资源',visible(){
        if (!this.getComponent(Sprite))return false;
        return this._transition === Transition.SPRITE;
    }})
    get pressedSprite () { return this._pressedSprite; }

    set pressedSprite (value: SpriteFrame | null) {
        if (this._pressedSprite !== value && this.getComponent(Sprite)) {
            this._pressedSprite = value;
            this._updateState();
        }
    }
    
    @property({type:SpriteFrame,tooltip:'禁用状态的对象图资源',visible(){
        if (!this.getComponent(Sprite)){return false;}
        return this._transition === Transition.SPRITE;
    }})
    get disabledSprite () { return this._disabledSprite; }

    set disabledSprite (value: SpriteFrame | null) {
        if (this._disabledSprite !== value && this.getComponent(Sprite)) {
            this._disabledSprite = value;
            this._updateState();
        }
    }

    @property({type:EventHandler})
    public clickEvent: EventHandler = new EventHandler();
    @property({serializable:true})
    protected _interactable = true;
    @property({serializable:true})
    protected _transition = Transition.SCALE;
    @property({serializable:true})
    protected _normalColor: Color = new Color(214, 214, 214, 255);
    @property({serializable:true})
    protected _pressColor: Color = Color.WHITE.clone();
    @property({serializable:true})
    protected _disabledColor: Color = new Color(124, 124, 124, 255);
    @property({serializable:true})
    protected _normalSprite: SpriteFrame | null = null;
    @property({serializable:true})
    protected _pressedSprite: SpriteFrame | null = null;
    @property({serializable:true})
    protected _disabledSprite: SpriteFrame | null = null;
    @property({serializable:true})
    protected _duration = 0.1;
    @property({serializable:true})
    protected _zoomScale = 1.1;
    @property({serializable:true})
    protected _target: Node | null = null;

    private _pressed = false;
    private _hovered = false;
    private _fromColor: Color = new Color();
    private _toColor: Color = new Color();
    private _time = 0;
    private _transitionFinished = true;
    private _fromScale: Vec3 = new Vec3();
    private _toScale: Vec3 = new Vec3();
    private _originalScale: Vec3 = new Vec3();
    private _sprite: Sprite | null = null;
    private _targetScale: Vec3 = new Vec3();
    private _isCanTouch:boolean = true;
    
    public bindClicket(target:Node,component:string,callFun:string,userData?:string) {
        if (App.isInvalid(target) || App.isEmpty(component) || App.isEmpty(callFun) || !this.interactable){
            return ;
        }
        
        const clickEvent = new EventHandler();
        clickEvent.target = target; // 这个 node 节点是你的事件处理代码组件所属的节点
        clickEvent.component = component;// 这个是代码文件名
        clickEvent.handler = callFun;
        clickEvent.customEventData = userData;
        this.clickEvent = clickEvent;
    }

    onLoad () {
        if (!this.target) { this.target = this.node; }

        const sprite = this.node.getComponent(Sprite);
        if (sprite) { this._normalSprite = sprite.spriteFrame; }

        this._applyTarget();
        this._updateState();
    }

    onEnable () {
        if (!EDITOR) {
            this._registerEvent();
        } else {
            this.node.on(Sprite.EventType.SPRITE_FRAME_CHANGED, (comp: Sprite) => {
                if (this._transition === Transition.SPRITE) {
                    this._normalSprite = comp.spriteFrame;
                } else {
                    this._normalSprite = null;
                    this._pressedSprite = null;
                    this._disabledSprite = null;
                }
            }, this);
        }
    }

    onDisable () {
        this._resetState();

        if (!EDITOR) {
            this.node.off(Input.EventType.TOUCH_START, this._onTouchBegan, this);
            this.node.off(Input.EventType.TOUCH_MOVE, this._onTouchMove, this);
            this.node.off(Input.EventType.TOUCH_END, this._onTouchEnded, this);
            this.node.off(Input.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
        } else {
            this.node.off(Sprite.EventType.SPRITE_FRAME_CHANGED);
        }
    }

    update (dt: number) {
        const target = this._target ? this._target : this.node;
        if (this._transitionFinished) {
            return;
        }

        if (this._transition !== Transition.COLOR && this._transition !== Transition.SCALE) {
            return;
        }

        this._time += dt;
        let ratio = 1.0;
        if (this._duration > 0) ratio = this._time / this._duration;

        if (ratio >= 1) {
            ratio = 1;
            this._transitionFinished = true;
        }

        if (this._transition === Transition.COLOR) {
            const renderComp = target.getComponent(UIRenderable);
            if (!renderComp) {
                return;
            }
            Color.lerp(_tempColor, this._fromColor, this._toColor, ratio);
            renderComp.color = _tempColor;
        } else if (this.transition === Transition.SCALE) {
            target.getScale(this._targetScale);
            this._targetScale.x = lerp(this._fromScale.x, this._toScale.x, ratio);
            this._targetScale.y = lerp(this._fromScale.y, this._toScale.y, ratio);
            target.setScale(this._targetScale);
        }
    }

    protected _resizeNodeToTargetNode () {
        let targetTrans = this._target && this._target._uiProps.uiTransformComp;
        if (EDITOR && targetTrans) {
            this.node._uiProps.uiTransformComp!.setContentSize(targetTrans.contentSize);
        }
    }

    protected _resetState () {
        this._pressed = false;
        this._hovered = false;
        const target = this._target;
        if (!target) return;

        const transition = this._transition;
        if (transition === Transition.COLOR && this._interactable) {
            const renderComp = target.getComponent(UIRenderable);
            if (!renderComp) return;
            renderComp.color = this._normalColor;
        } else if (transition === Transition.SCALE) {
            target.setScale(this._originalScale);
        }
        this._transitionFinished = true;
    }

    protected _registerEvent () {
        this.node.on(Input.EventType.TOUCH_START, this._onTouchBegan, this);
        this.node.on(Input.EventType.TOUCH_MOVE, this._onTouchMove, this);
        this.node.on(Input.EventType.TOUCH_END, this._onTouchEnded, this);
        this.node.on(Input.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
    }

    protected _getTargetSprite (target: Node | null) {
        let sprite: Sprite | null = null;
        if (target) sprite = target.getComponent(Sprite);
        return sprite;
    }

    protected _applyTarget () {
        this._sprite = this._getTargetSprite(this._target);
        this._target && Vec3.copy(this._originalScale, this._target.getScale());
    }

    // touch event handler
    protected _onTouchBegan (event?: EventTouch) {
        if (!this._interactable || !this.enabledInHierarchy || !this._isCanTouch) { return; }

        App.Audio.play(this.audioFile || this.audioClip);
        this._pressed = true;
        this._updateState();
        if (event) event.propagationStopped = true;
    }

    protected _onTouchMove (event?: EventTouch) {
        if (!this._interactable || !this.enabledInHierarchy || !this._pressed || !this._isCanTouch) { return; }
        if (!event) { return false; }

        const touch = (event as EventTouch).touch;
        if (!touch) { return false; }

        const hit = this.node._uiProps.uiTransformComp!.isHit(touch.getUILocation());

        if (this._transition === Transition.SCALE && this._target) {
            if (hit) {
                Vec3.copy(this._fromScale, this._originalScale);
                Vec3.multiplyScalar(this._toScale, this._originalScale, this._zoomScale);
                this._transitionFinished = false;
            } else {
                this._time = 0;
                this._transitionFinished = true;
                this._target && this._target.setScale(this._originalScale);
            }
        } else {
            const state = hit ? State.PRESSED : State.NORMAL;
            this._applyTransition(state);
        }

        event && (event.propagationStopped = true);
    }

    protected _onTouchEnded (event?: EventTouch) {
        if (!this._interactable || !this.enabledInHierarchy || !this._isCanTouch) return;

        if (this._pressed) {
            this._isCanTouch = false;
            this.hideNode && App.closeView(this.hideNode);
            this.clickEvent.target && EventHandler.emitEvents([this.clickEvent], event);
            this.node.emit('click', this);
            this.scheduleOnce(()=>{this._isCanTouch = true;},this.preventTime);
        }
        this._pressed = false;
        this._updateState();

        event && (event.propagationStopped = true);
    }

    protected _onTouchCancel (event?: EventTouch) {
        if (!this._interactable || !this.enabledInHierarchy || !this._isCanTouch) return;

        this._pressed = false;
        this._updateState();
    }

    // state handler
    protected _updateState () {
        const state = this._getButtonState();
        this._applyTransition(state);
    }

    protected _getButtonState () {
        let state = State.NORMAL;
        if (!this._interactable) state = State.DISABLED;
        else if (this._pressed) state = State.PRESSED;
        return state.toString();
    }

    protected _updateColorTransition (state: string) {
        const color = this[state + 'Color'];
        const target = this._target;
        if (!target) return;

        const renderComp = target.getComponent(UIRenderable);
        if (!renderComp) return;

        if (EDITOR || state === State.DISABLED) {
            renderComp.color = color;
        } else {
            this._fromColor = renderComp.color.clone();
            this._toColor = color;
            this._time = 0;
            this._transitionFinished = false;
        }
    }

    protected _updateSpriteTransition (state: string) {
        const sprite = this[state + 'Sprite'];
        if (this._sprite && sprite) this._sprite.spriteFrame = sprite;
    }

    protected _updateScaleTransition (state: string) {
        if (!this._interactable) return;
        state === State.PRESSED ? this._zoomUp():this._zoomBack();
    }

    protected _zoomUp () {
        Vec3.copy(this._fromScale, this._originalScale);
        Vec3.multiplyScalar(this._toScale, this._originalScale, this._zoomScale);
        this._time = 0;
        this._transitionFinished = false;
    }

    protected _zoomBack () {
        if (!this._target) return;

        Vec3.copy(this._fromScale, this._target.getScale());
        Vec3.copy(this._toScale, this._originalScale);
        this._time = 0;
        this._transitionFinished = false;
    }

    protected _applyTransition (state: string) {
        const transition = this._transition;
        if (transition === Transition.COLOR)
            this._updateColorTransition(state);
        else if (transition === Transition.SPRITE)
            this._updateSpriteTransition(state);
        else if (transition === Transition.SCALE)
            this._updateScaleTransition(state);
    }
}